//
//  Function.swift
//  cnswift-demo
//
//  Created by YangWei on 2020/12/23.
//  Copyright © 2020 YangWei. All rights reserved.
//

import UIKit

class Functions: SWDBaseObject {
    
    class func run() {
        
        let functions = Functions()
        
        // 1 定义和调用函数
        functions.definingAndCallingFunctions()
        
        // 2.1 无形式参数的函数
        functions.functionsWithoutParameters()
        
        // 2.2 多形式参数的函数
        functions.functionsWithMultipleParameters()
        
        // 2.3 无返回值的函数
        functions.functionsWithoutReturnValues()
        
        // 2.4 多返回值的函数
        functions.functionsWithMultipleReturnValues()
        
        // 2.5 可选元组返回类型
        functions.optionalTupleReturnTypes()
        
        // 2.6 隐式返回的函数
        functions.functionsWithAnImplicitReturn()
        
        // 3 函数实际参数标签和形式参数名
        functions.functionArgumentLabelsAndParameterNames()
        
        // 3.1 指定实际参数标签
        functions.specifyingArgumentLabels()
        
        // 3.2 省略实际参数标签
        functions.omittingArgumentLabels()
        
        // 3.3 默认形式参数值
        functions.defaultParameterValues()
        
        // 3.4 可变形式参数
        functions.variadicParameters()
        
        // 3.5 输入输出形式参数
        functions.inOutParameters()
        
        // 4 函数类型
        functions.functionTypes()
        
        // 4.1 使用函数类型
        functions.usingFunctionTypes()
        
        // 4.2 函数类型作为形式参数类型
        functions.functionTypesAsParameterTypes()
        
        // 4.3 函数类型作为返回类型
        functions.functionTypesAsReturnTypes()
        
        // 5 内嵌函数
        functions.nestedFunctions()
    }
    
    func greet(person: String) -> String {
        let greeting = "Hello, " + person + "!"
        return greeting
    }
    
    /* 将创建信息和返回语句组合到一行：*/
    func greetAgain(person: String) -> String {
        return "Hello again, " + person + "!"
    }
    
    /// 1 定义和调用函数
    func definingAndCallingFunctions() {
        let title = "1 定义和调用函数";
        printStartLine(title)

        print(greet(person: "Anna"))
        print(greet(person: "Brian"))
        
        print(greetAgain(person: "Anna"))
        
        
        printEndLine(title)
    }
    
    func sayHelloWorld() -> String {
        return "hello, world"
    }
    
    /// 2.1 无形式参数的函数
    func functionsWithoutParameters() {
        let title = "2.1 无形式参数的函数";
        printStartLine(title)
        
        print(sayHelloWorld())
        
        printEndLine(title)
    }
    
    func greet(person: String, alreadyGreeted: Bool) -> String {
        if alreadyGreeted {
            return greetAgain(person: person)
        } else {
            return greet(person: person)
        }
    }
    
    /// 2.2 多形式参数的函数
    func functionsWithMultipleParameters() {
        let title = "2.2 多形式参数的函数";
        printStartLine(title)
        
        print(greet(person: "Tim", alreadyGreeted: true))
        // Prints "Hello again, Tim!"
        
        printEndLine(title)
    }
    
    func printAndCount(string: String) -> Int {
        print(string)
        return string.count
    }
    
    func printWithoutCounting(string: String) {
        // let _ 可以忽略警告：Result of call to 'printAndCount(string:)' is unused
        let _ = printAndCount(string: string)
    }
    
    /// 2.3 无返回值的函数
    func functionsWithoutReturnValues() {
        let title = "2.3 无返回值的函数";
        printStartLine(title)
        
        /* 函数 greet(person:)表面没有定义返回值，实际会返回一个类型 Void，是一个空的元组，写作 () 。 */
        func greet(person: String) {
            print("Hello, \(person)!")
        }
        
        let r0: () = greet(person: "Dave") // "Hello, Dave!"
        print(r0) // ()
        
        let r1 = printAndCount(string: "hello, world") // "hello, world"
        print(r1) // 12
        
        let r2: () = printWithoutCounting(string: "hello, world") // "hello, world"
        print(r2) // ()
        
        printEndLine(title)
    }
    
    /* 通过元组让函数返回多个值 */
    func minMax(array: [Int]) -> (min: Int, max: Int) {
        var currentMin = array[0]
        var currentMax = array[0]
        for value in array[1..<array.count] {
            if value < currentMin {
                currentMin = value
            } else if value > currentMax {
                currentMax = value
            }
        }
        return (currentMin, currentMax)
    }
    
    /// 2.4 多返回值的函数
    func functionsWithMultipleReturnValues() {
        let title = "2.4 多返回值的函数";
        printStartLine(title)
        
        /* 元组的字段名在定义函数返回值时已经确定 */
        let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
        print("min is \(bounds.min) and max is \(bounds.max)")
        // "min is -6 and max is 109"
        
        printEndLine(title)
    }
    
    
    func minMaxV2(array: [Int]) -> (min: Int, max: Int)? {
        if array.isEmpty {
            return nil
        }
        var currentMin = array[0]
        var currentMax = array[0]
        for value in array[1..<array.count] {
            if value < currentMin {
                currentMin = value
            } else if value > currentMax {
                currentMax = value
            }
        }
        return (currentMin, currentMax)
    }
    
    /// 2.5 可选元组返回类型
    func optionalTupleReturnTypes() {
        let title = "2.5 可选元组返回类型";
        printStartLine(title)
        
        // 返回多个值可能为 nil
        let bounds2 = minMaxV2(array: [])
        print("min is \(bounds2?.min ?? 0) and max is \(bounds2?.max ?? 0)")
        
        printEndLine(title)
    }
    
    func greeting(for person: String) -> String {
        // 可以不用写 return
        "Hello, " + person + "!"
    }
    
    /// 2.6 隐式返回的函数
    func functionsWithAnImplicitReturn() {
        let title = "2.6 隐式返回的函数";
        printStartLine(title)
        
        print(greeting(for: "Dave")) // "Hello, Dave!"
        
        printEndLine(title)
    }
    
    /// 3 函数实际参数标签和形式参数名
    func functionArgumentLabelsAndParameterNames() {
        let title = "3 函数实际参数标签和形式参数名";
        printStartLine(title)
        
        func someFunction(firstParameterName: Int, secondParameterName: Int) {
            // firstParameterName 和 secondParameterName 是形参名
        }
        
        someFunction(firstParameterName: 1, secondParameterName: 2)
        
        printEndLine(title)
    }
    
    func greet(person: String, from hometown: String) -> String {
        return "Hello \(person)!  Glad you could visit from \(hometown)."
    }
    
    /// 3.1 指定实际参数标签
    func specifyingArgumentLabels() {
        let title = "3.1 指定实际参数标签";
        printStartLine(title)
        
        func someFunction(argumentLabel parameterName: Int) {
            // argumentLabel 为形参 parameterName 的实参标签
        }
        
        // "Hello Bill!  Glad you could visit from Cupertino."
        print(greet(person: "Bill", from: "Cupertino"))
        
        printEndLine(title)
    }
    
    /// 3.2 省略实际参数标签
    func omittingArgumentLabels() {
        let title = "3.2 省略实际参数标签";
        printStartLine(title)
        
        func someFunction(_ firstParameterName: Int, secondParameterName: Int) {
            // 添加 _ ，调用时候，可以省略实参标签 firstParameterName
        }
        someFunction(1, secondParameterName: 2)
        
        printEndLine(title)
    }
    
    /// 3.3 默认形式参数值
    func defaultParameterValues() {
        let title = "3.3 默认形式参数值";
        printStartLine(title)
        
        func someFunction(parameterWithDefault: Int = 12) {
            // 调用时，实参不传值时，默认实参值为 12
            print("parameterWithDefault = \(parameterWithDefault)")
        }
        someFunction(parameterWithDefault: 6) // 6
        someFunction() // 12
        
        printEndLine(title)
    }
    
    /// 求平均数
    func arithmeticMean(_ numbers: Double...) -> Double {
        var total: Double = 0
        for number in numbers {
            total += number
        }
        return total / Double(numbers.count)
    }
    
    /// 3.4 可变形式参数
    func variadicParameters() {
        let title = "3.4 可变形式参数";
        printStartLine(title)
        
        let r1 = arithmeticMean(1, 2, 3, 4, 5)
        print(r1) // 3.0
        
        let r2 = arithmeticMean(3, 8.25, 18.75)
        print(r2) // 10.0
        
        printEndLine(title)
    }
    
    /// 交换两个整数
    func swapTwoInts(_ a: inout Int, _ b: inout Int) {
        let temporaryA = a
        a = b
        b = temporaryA
    }
    
    /// 3.5 输入输出形式参数
    func inOutParameters() {
        let title = "3.5 输入输出形式参数";
        printStartLine(title)
        
        var someInt = 3
        var anotherInt = 107
        print("before swapTwoInts(): someInt = \(someInt), anotherInt = \(anotherInt)")
        swapTwoInts(&someInt, &anotherInt)
        print("after swapTwoInts(): someInt = \(someInt), anotherInt = \(anotherInt)")
        
        printEndLine(title)
    }
    
    /// 两个 Int 相加
    func addTwoInts(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
    
    /// 两个 Int 相乘
    func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
        return a * b
    }
    
    /// 4 函数类型
    func functionTypes() {
        let title = "4 函数类型";
        printStartLine(title)
        
        // 函数类型： () -> Void
        func printHelloWorld() {
            print("hello, world")
        }
        
        printEndLine(title)
    }
    
    /// 4.1 使用函数类型
    func usingFunctionTypes() {
        let title = "4.1 使用函数类型";
        printStartLine(title)
        
        var mathFunction: (Int, Int) -> Int = addTwoInts
        print("Result: \(mathFunction(2, 3))") // 5
        
        /* 不同的函数，如果相同函数类型，就可以指定相同的变量：*/
        mathFunction = multiplyTwoInts
        print("Result: \(mathFunction(2, 3))") // 6
        
        /* 指定一个函数为常量或者变量，Swift 会对类型进行推断：*/
        let anotherMathFunction = addTwoInts
        print(type(of: (anotherMathFunction))) // (Int, Int) -> Int
        
        printEndLine(title)
    }
    
    /// 打印一个 (Int, Int) -> Int 类型函数的执行结果
    func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
        print("Result: \(mathFunction(a, b))")
    }
    
    /// 4.2 函数类型作为形式参数类型
    func functionTypesAsParameterTypes() {
        let title = "4.2 函数类型作为形式参数类型";
        printStartLine(title)
        
        printMathResult(addTwoInts, 3, 5) // 8
        
        printEndLine(title)
    }
    
    /// +1
    func stepForward(_ input: Int) -> Int {
        return input + 1
    }
    
    /// -1
    func stepBackward(_ input: Int) -> Int {
        return input - 1
    }

    /// 选择 +1 或 -1
    func chooseStepFunction(backward: Bool) -> (Int) -> Int {
        return backward ? stepBackward : stepForward
    }
    
    /// 4.3 函数类型作为返回类型
    func functionTypesAsReturnTypes() {
        let title = "4.3 函数类型作为返回类型";
        printStartLine(title)
        
        var currentValue = 3
        // currentValue 大于 0，选择 stepBackward -1 函数
        let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)

        print("Counting to zero:")
        // Counting to zero:
        while currentValue != 0 {
            print("\(currentValue)... ")
            currentValue = moveNearerToZero(currentValue)
        }
        print("zero!")
        // 3...
        // 2...
        // 1...
        // zero!
        
        printEndLine(title)
    }
    
    /// 根据传入值，选择返回 +1或-1 内嵌函数
    func chooseStepFunctionV2(backward: Bool) -> (Int) -> Int {
        func stepForward(input: Int) -> Int {
            return input + 1
        }
        func stepBackward(input: Int) -> Int {
            return input - 1
        }
        return backward ? stepBackward : stepForward
    }
    
    /// 5 内嵌函数
    func nestedFunctions() {
        let title = "5 内嵌函数";
        printStartLine(title)
        
        
        var currentValue = -4
        // currentValue 小于0，选择 stepForward +1函数
        let moveNearerToZero = chooseStepFunctionV2(backward: currentValue > 0)
        while currentValue != 0 {
            print("\(currentValue)... ")
            currentValue = moveNearerToZero(currentValue)
        }
        print("zero!")
        
        printEndLine(title)
    }
}
